#pragma once

#include <set>
#include <boost/shared_ptr.hpp>
#include <boost/noncopyable.hpp>
#include <boostext/queue.hpp>
#include <Network/NetworkAccessManager.h>
#include <Network/HttpReply.h>
#include <services/scheduler.hpp>
#include <services/provider.hpp>

#include "exports.hpp"
#include "DataFilter.h"
#include "UrlReader.hpp"
#include "DataWriter.hpp"




class CRAWLER_API Crawler
	: public boost::noncopyable
{
public:

	typedef std::set<DataFilter::const_Ptr>   Filters;
	typedef QList<HTTPReply::Ptr>             Replies;

private:

	const QString              m_userAgent;
	const int                  m_numParallelRequests;

	services::scheduler::ptr   m_scheduler;
	services::task::ptr        m_networkTask;

	NetworkAccessManager       m_manager;

	//
	// Queues
	//

	DataWriter::Ptr            m_dataProvider;
	UrlReader::Ptr             m_urlProvider;

	QList<QUrl>                m_urls;


	//
	// Installed Filters
	//

	Filters                    m_filters;


	//
	// Requests
	//

	volatile quint64           m_completedRequests;
	volatile quint64           m_filteredReplies;

	Replies                    m_activeRequests;


	//
	// Networking Information
	//

	volatile float             m_currentRequests;
	QList<QTime>               m_requests;

	volatile float             m_currentDownload;
	QList<QPair<int,QTime>>    m_downloads;

	volatile float             m_currentUpload;
	QList<QPair<int,QTime>>    m_uploads;

public:

	/**
	 * Creates a new Crawler.
	 *
	 * The given providers are used to feed the crawler and to
	 * transport the data from the crawler to the plugins.
	 */
	Crawler(services::provider::const_ptr provider,
			DataWriter::Ptr dataProvider,
			UrlReader::Ptr urlProvider,
			QString userAgent,
			int parallelRequests);
	~Crawler();



	/**
	 * Add the given filter to this crawler.
	 */
	void add(DataFilter::const_Ptr filter);

	/**
	 * Remove the given filter from this crawler.
	 */
	void remove(DataFilter::const_Ptr filter);



	/**
	 * Get a list of all active Requests (their respective URLs).
	 */
	QList<QUrl> activeRequests();

	/**
	 * Get the number of completed requests.
	 */
	quint64 numCompletedRequests() const;

	/**
	 * Get the number of filtered-out requests.
	 */
	quint64 numFilteredReplies() const;

	/**
	 * Get the number of active requests.
	 */
	int numActiveRequests() const;



	/**
	 * Get the average of completed requests per second.
	 */
	float requestRate() const;

	/**
	 * Get the current download rate in kb/s
	 */
	float downloadRate() const;

	/**
	 * Get the current upload rate in kb/s
	 */
	float uploadRate() const;

private:

	/**
	 * Entry point for the networking thread.
	 */
	void run();

	/**
	 * Send a HTTP request to the given url's host.
	 */
	void get(const QUrl &url);

	/**
	 * Test if the given reply shall be filtered out.
	 */
	//Filter::Result filter(const QNetworkReply *reply) const;
	Filter::Result filter(const Reply& reply) const;

	void requestFinished(TCPSocket::Ptr reply);

	void update();
};
///////////////////////////////////////////////////////////////////////////////////////////////////
